home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 2 / Apprentice-Release2.iso / Tools / MPW / sed 2.0.3 / sed.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-01-19  |  42.0 KB  |  1,958 lines  |  [TEXT/MPS ]

  1. /*  GNU SED, a batch stream editor.
  2.     Copyright (C) 1989, 1990, 1991 Free Software Foundation, Inc.
  3.  
  4.     This program is free software; you can redistribute it and/or modify
  5.     it under the terms of the GNU General Public License as published by
  6.     the Free Software Foundation; either version 2, or (at your option)
  7.     any later version.
  8.  
  9.     This program is distributed in the hope that it will be useful,
  10.     but WITHOUT ANY WARRANTY; without even the implied warranty of
  11.     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12.     GNU General Public License for more details.
  13.  
  14.     You should have received a copy of the GNU General Public License
  15.     along with this program; if not, write to the Free Software
  16.     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  */
  17.  
  18. #ifdef __STDC__
  19. #define VOID void
  20. #else
  21. #define VOID char
  22. #endif
  23.  
  24.  
  25. #define _GNU_SOURCE
  26. #include <ctype.h>
  27. #ifndef isblank
  28. #define isblank(c) ((c) == ' ' || (c) == '\t')
  29. #endif
  30. #include <stdio.h>
  31. #ifndef macintosh
  32. #include <sys/types.h>
  33. #endif
  34. #include "rx.h"
  35. #include "getopt.h"
  36. #if defined(STDC_HEADERS)
  37. #include <stdlib.h>
  38. #endif
  39. #if HAVE_STRING_H || defined(STDC_HEADERS)
  40. #include <string.h>
  41. #ifndef bzero
  42. #define bzero(s, n)    memset ((s), 0, (n))
  43. #endif
  44. #if !defined(STDC_HEADERS)
  45. #include <memory.h>
  46. #endif
  47. #else
  48. #include <strings.h>
  49. #endif
  50.  
  51. #ifdef macintosh
  52. #include <errno.h>
  53. #endif
  54.  
  55. #ifdef MPW
  56. #include <CursorCtl.h>
  57. #endif
  58.  
  59. #ifdef RX_MEMDBUG
  60. #include <malloc.h>
  61. #endif
  62.  
  63. #ifndef HAVE_BCOPY
  64. #ifdef HAVE_MEMCPY
  65. #define bcopy(FROM,TO,LEN)  memcpy(TO,FROM,LEN)
  66. #else
  67. void
  68. bcopy (from, to, len)
  69.      char *from;
  70.      char *to;
  71.      int len;
  72. {
  73.   if (from < to)
  74.     {
  75.       from += len - 1;
  76.       to += len - 1;
  77.       while (len--)
  78.     *to-- = *from--;
  79.     }
  80.   else
  81.     while (len--)
  82.       *to++ = *from++;
  83. }
  84.  
  85. #endif
  86. #endif
  87.  
  88. char *version_string = "GNU sed version 2.03";
  89.  
  90. /* Struct vector is used to describe a chunk of a compiled sed program.  
  91.  * There is one vector for the main program, and one for each { } pair,
  92.  * and one for the entire program.  For {} blocks, RETURN_[VI] tells where
  93.  * to continue execution after this VECTOR.
  94.  */
  95.  
  96. struct vector
  97. {
  98.   struct sed_cmd *v;
  99.   int v_length;
  100.   int v_allocated;
  101.   struct vector *return_v;
  102.   int return_i;
  103. };
  104.  
  105.  
  106. /* Goto structure is used to hold both GOTO's and labels.  There are two
  107.  * separate lists, one of goto's, called 'jumps', and one of labels, called
  108.  * 'labels'.
  109.  * the V element points to the descriptor for the program-chunk in which the
  110.  * goto was encountered.
  111.  * the v_index element counts which element of the vector actually IS the
  112.  * goto/label.  The first element of the vector is zero.
  113.  * the NAME element is the null-terminated name of the label.
  114.  * next is the next goto/label in the list. 
  115.  */
  116.  
  117. struct sed_label
  118. {
  119.   struct vector *v;
  120.   int v_index;
  121.   char *name;
  122.   struct sed_label *next;
  123. };
  124.  
  125. /* ADDR_TYPE is zero for a null address,
  126.  *  one if addr_number is valid, or
  127.  * two if addr_regex is valid,
  128.  * three, if the address is '$'
  129.  * Other values are undefined.
  130.  */
  131.  
  132. enum addr_types
  133. {
  134.   addr_is_null = 0,
  135.   addr_is_num = 1,
  136.   addr_is_regex = 2,
  137.   addr_is_last = 3
  138. };
  139.  
  140. struct addr
  141. {
  142.   int addr_type;
  143.   struct re_pattern_buffer *addr_regex;
  144.   int addr_number;
  145. };
  146.  
  147.  
  148. /* Aflags:  If the low order bit is set, a1 has been
  149.  * matched; apply this command until a2 matches.
  150.  * If the next bit is set, apply this command to all
  151.  * lines that DON'T match the address(es).
  152.  */
  153.  
  154. #define A1_MATCHED_BIT    01
  155. #define ADDR_BANG_BIT    02
  156.  
  157. struct sed_cmd
  158. {
  159.   struct addr a1, a2;
  160.   int aflags;
  161.   
  162.   char cmd;
  163.   
  164.   union
  165.     {
  166.       /* This structure is used for a, i, and c commands */
  167.       struct
  168.     {
  169.       char *text;
  170.       int text_len;
  171.     }
  172.       cmd_txt;
  173.       
  174.       /* This is used for b and t commands */
  175.       struct sed_cmd *label;
  176.       
  177.       /* This for r and w commands */
  178.       FILE *io_file;
  179.       
  180.       /* This for the hairy s command */
  181.       /* For the flags var:
  182.      low order bit means the 'g' option was given,
  183.      next bit means the 'p' option was given,
  184.      and the next bit means a 'w' option was given,
  185.      and wio_file contains the file to write to. */
  186.       
  187. #define S_GLOBAL_BIT    01
  188. #define S_PRINT_BIT    02
  189. #define S_WRITE_BIT    04
  190. #define S_NUM_BIT    010
  191.       
  192.       struct
  193.     {
  194.       struct re_pattern_buffer *regx;
  195.       char *replacement;
  196.       int replace_length;
  197.       int flags;
  198.       int numb;
  199.       FILE *wio_file;
  200.     }
  201.       cmd_regex;
  202.       
  203.       /* This for the y command */
  204.       unsigned char *translate;
  205.       
  206.       /* For { */
  207.       struct vector *sub;
  208.       
  209.       /* for t and b */
  210.       struct sed_label *jump;
  211.     } x;
  212. };
  213.  
  214. /* Sed operates a line at a time. */
  215. struct line
  216. {
  217.   char *text;            /* Pointer to line allocated by malloc. */
  218.   int length;            /* Length of text. */
  219.   int alloc;            /* Allocated space for text. */
  220. };
  221.  
  222. /* This structure holds information about files opend by the 'r', 'w',
  223.    and 's///w' commands.  In paticular, it holds the FILE pointer to
  224.    use, the file's name, a flag that is non-zero if the file is being
  225.    read instead of written. */
  226.  
  227. #define NUM_FPS    32
  228. struct
  229.   {
  230.     FILE *phile;
  231.     char *name;
  232.     int readit;
  233.   }
  234.  
  235. file_ptrs[NUM_FPS];
  236.  
  237.  
  238. #if defined(__STDC__)
  239. # define P_(s) s
  240. #else
  241. # define P_(s) ()
  242. #endif
  243.  
  244. void close_files ();
  245. void panic P_ ((char *str,...));
  246. char *__fp_name P_ ((FILE * fp));
  247. FILE *ck_fopen P_ ((char *name, char *mode));
  248. void ck_fwrite P_ ((char *ptr, int size, int nmemb, FILE * stream));
  249. void ck_fclose P_ ((FILE * stream));
  250. VOID *ck_malloc P_ ((int size));
  251. VOID *ck_realloc P_ ((VOID * ptr, int size));
  252. char *ck_strdup P_ ((char *str));
  253. VOID *init_buffer P_ ((void));
  254. void flush_buffer P_ ((VOID * bb));
  255. int size_buffer P_ ((VOID * b));
  256. void add_buffer P_ ((VOID * bb, char *p, int n));
  257. void add1_buffer P_ ((VOID * bb, int ch));
  258. char *get_buffer P_ ((VOID * bb));
  259.  
  260. void compile_string P_ ((char *str));
  261. void compile_file P_ ((char *str));
  262. struct vector *compile_program P_ ((struct vector * vector, int));
  263. void bad_prog P_ ((char *why));
  264. int inchar P_ ((void));
  265. void savchar P_ ((int ch));
  266. int compile_address P_ ((struct addr * addr));
  267. char * last_regex_string = 0;
  268. void buffer_regex  P_ ((int slash));
  269. void compile_regex P_ ((void));
  270. struct sed_label *setup_jump P_ ((struct sed_label * list, struct sed_cmd * cmd, struct vector * vec));
  271. FILE *compile_filename P_ ((int readit));
  272. void read_file P_ ((char *name));
  273. void execute_program P_ ((struct vector * vec));
  274. int match_address P_ ((struct addr * addr));
  275. int read_pattern_space P_ ((void));
  276. void append_pattern_space P_ ((void));
  277. void line_copy P_ ((struct line * from, struct line * to));
  278. void line_append P_ ((struct line * from, struct line * to));
  279. void str_append P_ ((struct line * to, char *string, int length));
  280. void usage P_ ((int));
  281.  
  282. extern char *myname;
  283.  
  284. /* If set, don't write out the line unless explictly told to */
  285. int no_default_output = 0;
  286.  
  287. /* Current input line # */
  288. int input_line_number = 0;
  289.  
  290. /* Are we on the last input file? */
  291. int last_input_file = 0;
  292.  
  293. /* Have we hit EOF on the last input file?  This is used to decide if we
  294.    have hit the '$' address yet. */
  295. int input_EOF = 0;
  296.  
  297. /* non-zero if a quit command has been executed. */
  298. int quit_cmd = 0;
  299.  
  300. /* Have we done any replacements lately?  This is used by the 't' command. */
  301. int replaced = 0;
  302.  
  303. /* How many '{'s are we executing at the moment */
  304. int program_depth = 0;
  305.  
  306. /* The complete compiled SED program that we are going to run */
  307. struct vector *the_program = 0;
  308.  
  309. /* information about labels and jumps-to-labels.  This is used to do
  310.    the required backpatching after we have compiled all the scripts. */
  311. struct sed_label *jumps = 0;
  312. struct sed_label *labels = 0;
  313.  
  314. /* The 'current' input line. */
  315. struct line line;
  316.  
  317. /* An input line that's been stored by later use by the program */
  318. struct line hold;
  319.  
  320. /* A 'line' to append to the current line when it comes time to write it out */
  321. struct line append;
  322.  
  323.  
  324. /* When we're reading a script command from a string, 'prog_start' and
  325.    'prog_end' point to the beginning and end of the string.  This
  326.    would allow us to compile script strings that contain nulls, except
  327.    that script strings are only read from the command line, which is
  328.    null-terminated */
  329. unsigned char *prog_start;
  330. unsigned char *prog_end;
  331.  
  332. /* When we're reading a script command from a string, 'prog_cur' points
  333.    to the current character in the string */
  334. unsigned char *prog_cur;
  335.  
  336. /* This is the name of the current script file.
  337.    It is used for error messages. */
  338. char *prog_name;
  339.  
  340. /* This is the current script file.  If it is zero, we are reading
  341.    from a string stored in 'prog_start' instead.  If both 'prog_file'
  342.    and 'prog_start' are zero, we're in trouble! */
  343. FILE *prog_file;
  344.  
  345. /* this is the number of the current script line that we're compiling.  It is
  346.    used to give out useful and informative error messages. */
  347. int prog_line = 1;
  348.  
  349. /* This is the file pointer that we're currently reading data from.  It may
  350.    be stdin */
  351. FILE *input_file;
  352.  
  353. /* If this variable is non-zero at exit, one or more of the input
  354.    files couldn't be opened. */
  355.  
  356. int bad_input = 0;
  357.  
  358. /* 'an empty regular expression is equivalent to the last regular
  359.    expression read' so we have to keep track of the last regex used.
  360.    Here's where we store a pointer to it (it is only malloc()'d once) */
  361. struct re_pattern_buffer *last_regex;
  362.  
  363. /* Various error messages we may want to print */
  364. static char ONE_ADDR[] = "Command only uses one address";
  365. static char NO_ADDR[] = "Command doesn't take any addresses";
  366. static char LINE_JUNK[] = "Extra characters after command";
  367. static char BAD_EOF[] = "Unexpected End-of-file";
  368. static char NO_REGEX[] = "No previous regular expression";
  369. static char NO_COMMAND[] = "Missing command";
  370.  
  371. static struct option longopts[] =
  372. {
  373.   {"expression", 1, NULL, 'e'},
  374.   {"file", 1, NULL, 'f'},
  375.   {"quiet", 0, NULL, 'n'},
  376.   {"silent", 0, NULL, 'n'},
  377.   {"version", 0, NULL, 'V'},
  378.   {"help", 0, NULL, 'h'},
  379.   {NULL, 0, NULL, 0}
  380. };
  381.  
  382. void
  383. main (argc, argv)
  384.      int argc;
  385.      char **argv;
  386. {
  387.   int opt;
  388.   char *e_strings = NULL;
  389.   int compiled = 0;
  390.   struct sed_label *go, *lbl;
  391.  
  392. #ifdef MPW
  393.   InitCursorCtl (NULL);
  394.   SpinCursor (1);
  395. #endif
  396.   /* see regex.h */
  397.   re_set_syntax (RE_SYNTAX_POSIX_BASIC);
  398.   rx_cache_bound = 10000;    /* Consume memory rampantly. */
  399.  
  400.   myname = argv[0];
  401.   while ((opt = getopt_long (argc, argv, "hne:f:V", longopts, (int *) 0))
  402.      != EOF)
  403.     {
  404.       switch (opt)
  405.     {
  406.     case 'n':
  407.       no_default_output = 1;
  408.       break;
  409.     case 'e':
  410.       if (e_strings == NULL)
  411.         {
  412.           e_strings = ck_malloc (strlen (optarg) + 2);
  413.           strcpy (e_strings, optarg);
  414.         }
  415.       else
  416.         {
  417.           e_strings = ck_realloc (e_strings, strlen (e_strings) + strlen (optarg) + 2);
  418.           strcat (e_strings, optarg);
  419.         }
  420.       strcat (e_strings, "\n");
  421.       compiled = 1;
  422.       break;
  423.     case 'f':
  424.       compile_file (optarg);
  425.       compiled = 1;
  426.       break;
  427.     case 'V':
  428.       fprintf (stderr, "%s\n", version_string);
  429.       exit (0);
  430.       break;
  431.     case 'h':
  432.       usage (0);
  433.       break;
  434.     default:
  435.       usage (4);
  436.       break;
  437.     }
  438.     }
  439.   if (e_strings)
  440.     {
  441.       compile_string (e_strings);
  442.       free (e_strings);
  443.     }
  444.   if (!compiled)
  445.     {
  446.       if (optind == argc)
  447.     usage (4);
  448.       compile_string (argv[optind++]);
  449.     }
  450.  
  451.   for (go = jumps; go; go = go->next)
  452.     {
  453.       for (lbl = labels; lbl; lbl = lbl->next)
  454.     if (!strcmp (lbl->name, go->name))
  455.       break;
  456.       if (*go->name && !lbl)
  457.     panic ("Can't find label for jump to '%s'", go->name);
  458.       go->v->v[go->v_index].x.jump = lbl;
  459.     }
  460.  
  461.   line.length = 0;
  462.   line.alloc = 50;
  463.   line.text = ck_malloc (50);
  464.  
  465.   append.length = 0;
  466.   append.alloc = 50;
  467.   append.text = ck_malloc (50);
  468.  
  469.   hold.length = 1;
  470.   hold.alloc = 50;
  471.   hold.text = ck_malloc (50);
  472.   hold.text[0] = '\n';
  473.  
  474.   if (argc <= optind)
  475.     {
  476.       last_input_file++;
  477.       read_file ("-");
  478.     }
  479.   else
  480.     while (optind < argc)
  481.       {
  482.     if (optind == argc - 1)
  483.       last_input_file++;
  484.     read_file (argv[optind]);
  485.     optind++;
  486.     if (quit_cmd)
  487.       break;
  488.       }
  489.   close_files ();
  490.   if (bad_input)
  491.     exit (2);
  492.   exit (0);
  493. }
  494.  
  495. void
  496. close_files ()
  497. {
  498.   int nf;
  499.  
  500.   for (nf = 0; nf < NUM_FPS; nf++)
  501.     {
  502.       if (file_ptrs[nf].phile)
  503.     fclose (file_ptrs[nf].phile);
  504.     }
  505. }
  506.  
  507. /* 'str' is a string (from the command line) that contains a sed command.
  508.    Compile the command, and add it to the end of 'the_program' */
  509. void
  510. compile_string (str)
  511.      char *str;
  512. {
  513.   prog_file = 0;
  514.   prog_line = 0;
  515.   prog_start = prog_cur = (unsigned char *)str;
  516.   prog_end = (unsigned char *)str + strlen (str);
  517.   the_program = compile_program (the_program, prog_line);
  518. }
  519.  
  520. /* 'str' is the name of a file containing sed commands.  Read them in
  521.    and add them to the end of 'the_program' */
  522. void
  523. compile_file (str)
  524.      char *str;
  525. {
  526.   int ch;
  527.  
  528.   prog_start = prog_cur = prog_end = 0;
  529.   prog_name = str;
  530.   prog_line = 1;
  531.   if (str[0] == '-' && str[1] == '\0')
  532.     prog_file = stdin;
  533.   else
  534.     prog_file = ck_fopen (str, "r");
  535.   ch = getc (prog_file);
  536.   if (ch == '#')
  537.     {
  538.       ch = getc (prog_file);
  539.       if (ch == 'n')
  540.     no_default_output++;
  541.       while (ch != EOF && ch != '\n')
  542.     {
  543.       ch = getc (prog_file);
  544.       if (ch == '\\')
  545.         ch = getc (prog_file);
  546.     }
  547.       ++prog_line;
  548.     }
  549.   else if (ch != EOF)
  550.     ungetc (ch, prog_file);
  551.   the_program = compile_program (the_program, prog_line);
  552. }
  553.  
  554. #define MORE_CMDS 40
  555.  
  556. /* Read a program (or a subprogram within '{' '}' pairs) in and store
  557.    the compiled form in *'vector'  Return a pointer to the new vector.  */
  558. struct vector *
  559. compile_program (vector, open_line)
  560.      struct vector *vector;
  561.      int open_line;
  562. {
  563.   struct sed_cmd *cur_cmd;
  564.   int ch = 0;
  565.   int pch;
  566.   int slash;
  567.   VOID *b;
  568.   unsigned char *string;
  569.   int num;
  570.  
  571.   if (!vector)
  572.     {
  573.       vector = (struct vector *) ck_malloc (sizeof (struct vector));
  574.       vector->v = (struct sed_cmd *) ck_malloc (MORE_CMDS * sizeof (struct sed_cmd));
  575.       vector->v_allocated = MORE_CMDS;
  576.       vector->v_length = 0;
  577.       vector->return_v = 0;
  578.       vector->return_i = 0;
  579.     }
  580.   for (;;)
  581.     {
  582. #ifdef MPW
  583.     SpinCursor (1);
  584. #endif
  585.     skip_comment:
  586.       do
  587.     {
  588.       pch = ch;
  589.       ch = inchar ();
  590.       if ((pch == '\\') && (ch == '\n'))
  591.         ch = inchar ();
  592.     }
  593.       while (ch != EOF && (isblank (ch) || ch == '\n' || ch == ';'));
  594.       if (ch == EOF)
  595.     break;
  596.       savchar (ch);
  597.  
  598.       if (vector->v_length == vector->v_allocated)
  599.     {
  600.       vector->v = ((struct sed_cmd *)
  601.                ck_realloc ((VOID *) vector->v,
  602.                    ((vector->v_length + MORE_CMDS)
  603.                     * sizeof (struct sed_cmd))));
  604.       vector->v_allocated += MORE_CMDS;
  605.     }
  606.       cur_cmd = vector->v + vector->v_length;
  607.       vector->v_length++;
  608.  
  609.       cur_cmd->a1.addr_type = 0;
  610.       cur_cmd->a2.addr_type = 0;
  611.       cur_cmd->aflags = 0;
  612.       cur_cmd->cmd = 0;
  613.  
  614.       if (compile_address (&(cur_cmd->a1)))
  615.     {
  616.       ch = inchar ();
  617.       if (ch == ',')
  618.         {
  619.           do
  620.         ch = inchar ();
  621.           while (ch != EOF && isblank (ch));
  622.           savchar (ch);
  623.           if (compile_address (&(cur_cmd->a2)))
  624.         ;
  625.           else
  626.         bad_prog ("Unexpected ','");
  627.         }
  628.       else
  629.         savchar (ch);
  630.     }
  631.       if (cur_cmd->a1.addr_type == addr_is_num
  632.       && cur_cmd->a2.addr_type == addr_is_num
  633.       && cur_cmd->a2.addr_number < cur_cmd->a1.addr_number)
  634.     cur_cmd->a2.addr_number = cur_cmd->a1.addr_number;
  635.  
  636.       ch = inchar ();
  637.       if (ch == EOF)
  638.     bad_prog (NO_COMMAND);
  639.     new_cmd:
  640.       switch (ch)
  641.     {
  642.     case '#':
  643.       if (cur_cmd->a1.addr_type != 0)
  644.         bad_prog (NO_ADDR);
  645.       do
  646.         {
  647.           ch = inchar ();
  648.           if (ch == '\\')
  649.         ch = inchar ();
  650.         }
  651.       while (ch != EOF && ch != '\n');
  652.       vector->v_length--;
  653.       goto skip_comment;
  654.     case '!':
  655.       if (cur_cmd->aflags & ADDR_BANG_BIT)
  656.         bad_prog ("Multiple '!'s");
  657.       cur_cmd->aflags |= ADDR_BANG_BIT;
  658.       do
  659.         ch = inchar ();
  660.       while (ch != EOF && isblank (ch));
  661.       if (ch == EOF)
  662.         bad_prog (NO_COMMAND);
  663. #if 0
  664.       savchar (ch);
  665. #endif
  666.       goto new_cmd;
  667.     case 'a':
  668.     case 'i':
  669.       if (cur_cmd->a2.addr_type != 0)
  670.         bad_prog (ONE_ADDR);
  671.       /* Fall Through */
  672.     case 'c':
  673.       cur_cmd->cmd = ch;
  674.       if (inchar () != '\\' || inchar () != '\n')
  675.         bad_prog (LINE_JUNK);
  676.       b = init_buffer ();
  677.       while ((ch = inchar ()) != EOF && ch != '\n')
  678.         {
  679.           if (ch == '\\')
  680.         ch = inchar ();
  681.           add1_buffer (b, ch);
  682.         }
  683.       if (ch != EOF)
  684.         add1_buffer (b, ch);
  685.       num = size_buffer (b);
  686.       string = (unsigned char *) ck_malloc (num);
  687.       bcopy (get_buffer (b), string, num);
  688.       flush_buffer (b);
  689.       cur_cmd->x.cmd_txt.text_len = num;
  690.       cur_cmd->x.cmd_txt.text = (char *) string;
  691.       break;
  692.     case '{':
  693.       cur_cmd->cmd = ch;
  694.       program_depth++;
  695. #if 0
  696.       while ((ch = inchar ()) != EOF && ch != '\n')
  697.         if (!isblank (ch))
  698.           bad_prog (LINE_JUNK);
  699. #endif
  700.       cur_cmd->x.sub = compile_program ((struct vector *) 0, prog_line);
  701.       /* FOO JF is this the right thing to do?
  702.                almost.  don't forget a return addr.  -t */
  703.       cur_cmd->x.sub->return_v = vector;
  704.       cur_cmd->x.sub->return_i = vector->v_length - 1;
  705.       break;
  706.     case '}':
  707.       if (!program_depth)
  708.         bad_prog ("Unexpected '}'");
  709.       --program_depth;
  710.       /* a return insn for subprograms -t */
  711.       cur_cmd->cmd = ch;
  712.       if (cur_cmd->a1.addr_type != 0)
  713.         bad_prog ("} doesn't want any addresses");
  714.       while ((ch = inchar ()) != EOF && ch != '\n' && ch != ';')
  715.         if (!isblank (ch))
  716.           bad_prog (LINE_JUNK);
  717.       return vector;
  718.     case ':':
  719.       cur_cmd->cmd = ch;
  720.       if (cur_cmd->a1.addr_type != 0)
  721.         bad_prog (": doesn't want any addresses");
  722.       labels = setup_jump (labels, cur_cmd, vector);
  723.       break;
  724.     case 'b':
  725.     case 't':
  726.       cur_cmd->cmd = ch;
  727.       jumps = setup_jump (jumps, cur_cmd, vector);
  728.       break;
  729.     case 'q':
  730.     case '=':
  731.       if (cur_cmd->a2.addr_type)
  732.         bad_prog (ONE_ADDR);
  733.       /* Fall Through */
  734.     case 'd':
  735.     case 'D':
  736.     case 'g':
  737.     case 'G':
  738.     case 'h':
  739.     case 'H':
  740.     case 'l':
  741.     case 'n':
  742.     case 'N':
  743.     case 'p':
  744.     case 'P':
  745.     case 'x':
  746.       cur_cmd->cmd = ch;
  747.       do
  748.         ch = inchar ();
  749.       while (ch != EOF && isblank (ch) && ch != '\n' && ch != ';');
  750.       if (ch != '\n' && ch != ';' && ch != EOF)
  751.         bad_prog (LINE_JUNK);
  752.       break;
  753.  
  754.     case 'r':
  755.       if (cur_cmd->a2.addr_type != 0)
  756.         bad_prog (ONE_ADDR);
  757.       /* FALL THROUGH */
  758.     case 'w':
  759.       cur_cmd->cmd = ch;
  760.       cur_cmd->x.io_file = compile_filename (ch == 'r');
  761.       break;
  762.  
  763.     case 's':
  764.       cur_cmd->cmd = ch;
  765.       slash = inchar ();
  766.       buffer_regex (slash);
  767.       compile_regex ();
  768.  
  769.       cur_cmd->x.cmd_regex.regx = last_regex;
  770.  
  771.       b = init_buffer ();
  772.       while (((ch = inchar ()) != EOF) && (ch != slash) && (ch != '\n'))
  773.         {
  774.           if (ch == '\\')
  775.         {
  776.           int ci;
  777.  
  778.           ci = inchar ();
  779.           if (ci != EOF)
  780.             {
  781.               if (ci != '\n')
  782.             add1_buffer (b, ch);
  783.               add1_buffer (b, ci);
  784.             }
  785.         }
  786.           else
  787.         add1_buffer (b, ch);
  788.         }
  789.       if (ch != slash)
  790.         {
  791.           if (ch == '\n' && prog_line > 1)
  792.         --prog_line;
  793.           bad_prog ("Unterminated `s' command");
  794.         }
  795.       cur_cmd->x.cmd_regex.replace_length = size_buffer (b);
  796.       cur_cmd->x.cmd_regex.replacement = ck_malloc (cur_cmd->x.cmd_regex.replace_length);
  797.       bcopy (get_buffer (b), cur_cmd->x.cmd_regex.replacement, cur_cmd->x.cmd_regex.replace_length);
  798.       flush_buffer (b);
  799.  
  800.       cur_cmd->x.cmd_regex.flags = 0;
  801.       cur_cmd->x.cmd_regex.numb = 0;
  802.  
  803.       if (ch == EOF)
  804.         break;
  805.       do
  806.         {
  807.           ch = inchar ();
  808.           switch (ch)
  809.         {
  810.         case 'p':
  811.           if (cur_cmd->x.cmd_regex.flags & S_PRINT_BIT)
  812.             bad_prog ("multiple 'p' options to 's' command");
  813.           cur_cmd->x.cmd_regex.flags |= S_PRINT_BIT;
  814.           break;
  815.         case 'g':
  816.           if (cur_cmd->x.cmd_regex.flags & S_NUM_BIT)
  817.             cur_cmd->x.cmd_regex.flags &= ~S_NUM_BIT;
  818.           if (cur_cmd->x.cmd_regex.flags & S_GLOBAL_BIT)
  819.             bad_prog ("multiple 'g' options to 's' command");
  820.           cur_cmd->x.cmd_regex.flags |= S_GLOBAL_BIT;
  821.           break;
  822.         case 'w':
  823.           cur_cmd->x.cmd_regex.flags |= S_WRITE_BIT;
  824.           cur_cmd->x.cmd_regex.wio_file = compile_filename (0);
  825.           ch = '\n';
  826.           break;
  827.         case '0':
  828.         case '1':
  829.         case '2':
  830.         case '3':
  831.         case '4':
  832.         case '5':
  833.         case '6':
  834.         case '7':
  835.         case '8':
  836.         case '9':
  837.           if (cur_cmd->x.cmd_regex.flags & S_NUM_BIT)
  838.             bad_prog ("multiple number options to 's' command");
  839.           if ((cur_cmd->x.cmd_regex.flags & S_GLOBAL_BIT) == 0)
  840.             cur_cmd->x.cmd_regex.flags |= S_NUM_BIT;
  841.           num = 0;
  842.           while (isdigit (ch))
  843.             {
  844.               num = num * 10 + ch - '0';
  845.               ch = inchar ();
  846.             }
  847.           savchar (ch);
  848.           cur_cmd->x.cmd_regex.numb = num;
  849.           break;
  850.         case '\n':
  851.         case ';':
  852.         case EOF:
  853.           break;
  854.         default:
  855.           bad_prog ("Unknown option to 's'");
  856.           break;
  857.         }
  858.         }
  859.       while (ch != EOF && ch != '\n' && ch != ';');
  860.       if (ch == EOF)
  861.         break;
  862.       break;
  863.  
  864.     case 'y':
  865.       cur_cmd->cmd = ch;
  866.       string = (unsigned char *) ck_malloc (256);
  867.       for (num = 0; num < 256; num++)
  868.         string[num] = num;
  869.       b = init_buffer ();
  870.       slash = inchar ();
  871.       while ((ch = inchar ()) != EOF && ch != slash)
  872.         add1_buffer (b, ch);
  873.       cur_cmd->x.translate = string;
  874.       string = (unsigned char *) get_buffer (b);
  875.       for (num = size_buffer (b); num; --num)
  876.         {
  877.           ch = inchar ();
  878.           if (ch == EOF)
  879.         bad_prog (BAD_EOF);
  880.           if (ch == slash)
  881.         bad_prog ("strings for y command are different lengths");
  882.           cur_cmd->x.translate[*string++] = ch;
  883.         }
  884.       flush_buffer (b);
  885.       if (inchar () != slash || ((ch = inchar ()) != EOF && ch != '\n' && ch != ';'))
  886.         bad_prog (LINE_JUNK);
  887.       break;
  888.  
  889.     default:
  890.       bad_prog ("Unknown command");
  891.     }
  892.     }
  893.   if (program_depth)
  894.     {
  895.       prog_line = open_line;
  896.       bad_prog ("Unmatched `{'");
  897.     }
  898.   return vector;
  899. }
  900.  
  901. /* Complain about a programming error and exit. */
  902. void
  903. bad_prog (why)
  904.      char *why;
  905. {
  906. #ifdef MPW
  907.   if (prog_line)
  908.     fprintf (stderr, "File '%s'; Line %d\t# %s - %s\n",
  909.          prog_name, prog_line, myname, why);
  910.   else
  911.     fprintf (stderr, "### %s - %s\n", myname, why);
  912. #else
  913.   if (prog_line)
  914.     fprintf (stderr, "%s: file %s line %d: %s\n",
  915.          myname, prog_name, prog_line, why);
  916.   else
  917.     fprintf (stderr, "%s: %s\n", myname, why);
  918. #endif
  919.   exit (1);
  920. }
  921.  
  922. /* Read the next character from the program.  Return EOF if there isn't
  923.    anything to read.  Keep prog_line up to date, so error messages can
  924.    be meaningful. */
  925. int
  926. inchar ()
  927. {
  928.   int ch;
  929.   if (prog_file)
  930.     {
  931.       if (feof (prog_file))
  932.     return EOF;
  933.       else
  934.     ch = getc (prog_file);
  935.     }
  936.   else
  937.     {
  938.       if (!prog_cur)
  939.     return EOF;
  940.       else if (prog_cur == prog_end)
  941.     {
  942.       ch = EOF;
  943.       prog_cur = 0;
  944.     }
  945.       else
  946.     ch = *prog_cur++;
  947.     }
  948.   if ((ch == '\n') && prog_line)
  949.     prog_line++;
  950.   return ch;
  951. }
  952.  
  953. /* unget 'ch' so the next call to inchar will return it.  'ch' must not be
  954.    EOF or anything nasty like that. */
  955. void
  956. savchar (ch)
  957.      int ch;
  958. {
  959.   if (ch == EOF)
  960.     return;
  961.   if (ch == '\n' && prog_line > 1)
  962.     --prog_line;
  963.   if (prog_file)
  964.     ungetc (ch, prog_file);
  965.   else
  966.     *--prog_cur = ch;
  967. }
  968.  
  969.  
  970. /* Try to read an address for a sed command.  If it succeeeds,
  971.    return non-zero and store the resulting address in *'addr'.
  972.    If the input doesn't look like an address read nothing
  973.    and return zero. */
  974. int
  975. compile_address (addr)
  976.      struct addr *addr;
  977. {
  978.   int ch;
  979.   int num;
  980.  
  981.   ch = inchar ();
  982.  
  983.   if (isdigit (ch))
  984.     {
  985.       num = ch - '0';
  986.       while ((ch = inchar ()) != EOF && isdigit (ch))
  987.     num = num * 10 + ch - '0';
  988.       while (ch != EOF && isblank (ch))
  989.     ch = inchar ();
  990.       savchar (ch);
  991.       addr->addr_type = addr_is_num;
  992.       addr->addr_number = num;
  993.       return 1;
  994.     }
  995.   else if (ch == '/' || ch == '\\')
  996.     {
  997.       addr->addr_type = addr_is_regex;
  998.       if (ch == '\\')
  999.     ch = inchar ();
  1000.       buffer_regex (ch);
  1001.       compile_regex ();
  1002.       addr->addr_regex = last_regex;
  1003.       do
  1004.     ch = inchar ();
  1005.       while (ch != EOF && isblank (ch));
  1006.       savchar (ch);
  1007.       return 1;
  1008.     }
  1009.   else if (ch == '$')
  1010.     {
  1011.       addr->addr_type = addr_is_last;
  1012.       do
  1013.     ch = inchar ();
  1014.       while (ch != EOF && isblank (ch));
  1015.       savchar (ch);
  1016.       return 1;
  1017.     }
  1018.   else
  1019.     savchar (ch);
  1020.   return 0;
  1021. }
  1022.  
  1023. void
  1024. buffer_regex (slash)
  1025.      int slash;
  1026. {
  1027.   VOID *b;
  1028.   int ch;
  1029.   int char_class_pos = -1;
  1030.  
  1031.   b = init_buffer ();
  1032.   while ((ch = inchar ()) != EOF && (ch != slash || (char_class_pos >= 0)))
  1033.     {
  1034.       if (ch == '^')
  1035.     {
  1036.       if (size_buffer (b) == 0)
  1037.         {
  1038.           add1_buffer (b, '\\');
  1039.           add1_buffer (b, '`');
  1040.         }
  1041.       else
  1042.         add1_buffer (b, ch);
  1043.       continue;
  1044.     }
  1045.       else if (ch == '$')
  1046.     {
  1047.       ch = inchar ();
  1048.       savchar (ch);
  1049.       if (ch == slash)
  1050.         {
  1051.           add1_buffer (b, '\\');
  1052.           add1_buffer (b, '\'');
  1053.         }
  1054.       else
  1055.         add1_buffer (b, '$');
  1056.       continue;
  1057.     }
  1058.       else if (ch == '[')
  1059.     {
  1060.       if (char_class_pos < 0)
  1061.         char_class_pos = size_buffer (b);
  1062.       add1_buffer (b, ch);
  1063.       continue;
  1064.     }
  1065.       else if (ch == ']')
  1066.     {
  1067.       add1_buffer (b, ch);
  1068.       {
  1069.         char * regexp = get_buffer (b);
  1070.         int pos = size_buffer (b) - 1;
  1071.         if (!(   (char_class_pos >= 0)
  1072.           && (   (pos == char_class_pos + 1)
  1073.               || (   (pos == char_class_pos + 2)
  1074.               && (regexp[char_class_pos + 1] == '^')))))
  1075.           char_class_pos = -1;
  1076.         continue;
  1077.       }
  1078.     }
  1079.       else if (ch != '\\' || (char_class_pos >= 0))
  1080.     {
  1081.       add1_buffer (b, ch);
  1082.       continue;
  1083.     }
  1084.       ch = inchar ();
  1085.       switch (ch)
  1086.     {
  1087.     case 'n':
  1088.       add1_buffer (b, '\n');
  1089.       break;
  1090. #if 0
  1091.     case 'b':
  1092.       add1_buffer (b, '\b');
  1093.       break;
  1094.     case 'f':
  1095.       add1_buffer (b, '\f');
  1096.       break;
  1097.     case 'r':
  1098.       add1_buffer (b, '\r');
  1099.       break;
  1100.     case 't':
  1101.       add1_buffer (b, '\t');
  1102.       break;
  1103. #endif /* 0 */
  1104.     case EOF:
  1105.       break;
  1106.     default:
  1107.       add1_buffer (b, '\\');
  1108.       add1_buffer (b, ch);
  1109.       break;
  1110.     }
  1111.     }
  1112.   if (ch == EOF)
  1113.     bad_prog (BAD_EOF);
  1114.   if (size_buffer (b))
  1115.     {
  1116.       if (last_regex_string)
  1117.     last_regex_string = (char *)ck_realloc (last_regex_string,
  1118.                         size_buffer (b) + 1);
  1119.       else
  1120.     last_regex_string = (char *)ck_malloc (size_buffer (b) + 1);
  1121.       bcopy (get_buffer (b), last_regex_string, size_buffer (b));
  1122.       last_regex_string [size_buffer (b)] = 0;
  1123.     }
  1124.   else if (!last_regex)
  1125.     bad_prog (NO_REGEX);
  1126.   flush_buffer (b);
  1127. }
  1128.  
  1129. void
  1130. compile_regex ()
  1131. {
  1132.   const char * error;
  1133.   last_regex = ((struct re_pattern_buffer *)
  1134.         ck_malloc (sizeof (struct re_pattern_buffer)));
  1135.   bzero (last_regex, sizeof (*last_regex));
  1136.   last_regex->fastmap = ck_malloc (256);
  1137.   error = re_compile_pattern (last_regex_string,
  1138.                   strlen (last_regex_string), last_regex);
  1139.   if (error)
  1140.     bad_prog ((char *)error);
  1141. }
  1142.  
  1143. /* Store a label (or label reference) created by a ':', 'b', or 't'
  1144.    comand so that the jump to/from the lable can be backpatched after
  1145.    compilation is complete */
  1146. struct sed_label *
  1147. setup_jump (list, cmd, vec)
  1148.      struct sed_label *list;
  1149.      struct sed_cmd *cmd;
  1150.      struct vector *vec;
  1151. {
  1152.   struct sed_label *tmp;
  1153.   VOID *b;
  1154.   int ch;
  1155.  
  1156.   b = init_buffer ();
  1157.   while ((ch = inchar ()) != EOF && isblank (ch))
  1158.     ;
  1159.   /* Possible non posixicity. */
  1160.   while (ch != EOF && ch != '\n' && (!isblank (ch)) && ch != ';' && ch != '}')
  1161.     {
  1162.       add1_buffer (b, ch);
  1163.       ch = inchar ();
  1164.     }
  1165.   savchar (ch);
  1166.   add1_buffer (b, '\0');
  1167.   tmp = (struct sed_label *) ck_malloc (sizeof (struct sed_label));
  1168.   tmp->v = vec;
  1169.   tmp->v_index = cmd - vec->v;
  1170.   tmp->name = ck_strdup (get_buffer (b));
  1171.   tmp->next = list;
  1172.   flush_buffer (b);
  1173.   return tmp;
  1174. }
  1175.  
  1176. /* read in a filename for a 'r', 'w', or 's///w' command, and
  1177.    update the internal structure about files.  The file is
  1178.    opened if it isn't already open. */
  1179. FILE *
  1180. compile_filename (readit)
  1181.      int readit;
  1182. {
  1183.   char *file_name;
  1184.   int n;
  1185.   VOID *b;
  1186.   int ch;
  1187.  
  1188.   if (inchar () != ' ')
  1189.     bad_prog ("missing ' ' before filename");
  1190.   b = init_buffer ();
  1191.   while ((ch = inchar ()) != EOF && ch != '\n')
  1192.     add1_buffer (b, ch);
  1193.   add1_buffer (b, '\0');
  1194.   file_name = get_buffer (b);
  1195.   for (n = 0; n < NUM_FPS; n++)
  1196.     {
  1197.       if (!file_ptrs[n].name)
  1198.     break;
  1199.       if (!strcmp (file_ptrs[n].name, file_name))
  1200.     {
  1201.       if (file_ptrs[n].readit != readit)
  1202.         bad_prog ("Can't open file for both reading and writing");
  1203.       flush_buffer (b);
  1204.       return file_ptrs[n].phile;
  1205.     }
  1206.     }
  1207.   if (n < NUM_FPS)
  1208.     {
  1209.       file_ptrs[n].name = ck_strdup (file_name);
  1210.       file_ptrs[n].readit = readit;
  1211.       if (!readit)
  1212.     file_ptrs[n].phile = ck_fopen (file_name, "w");
  1213.       else
  1214.     {
  1215.       file_ptrs[n].phile = ck_fopen (file_name, "r");
  1216.     }
  1217.       flush_buffer (b);
  1218.       return file_ptrs[n].phile;
  1219.     }
  1220.   else
  1221.     {
  1222.       bad_prog ("Hopelessely evil compiled in limit on number of open files.  re-compile sed");
  1223.       return 0;
  1224.     }
  1225. }
  1226.  
  1227. /* Read a file and apply the compiled script to it. */
  1228. void
  1229. read_file (name)
  1230.      char *name;
  1231. {
  1232.   if (*name == '-' && name[1] == '\0')
  1233.     input_file = stdin;
  1234.   else
  1235.     {
  1236.       input_file = fopen (name, "r");
  1237.       if (input_file == 0)
  1238.     {
  1239. #ifdef macintosh
  1240.       fprintf (stderr, "### %s - Can't read %s: %s\n", myname, name,
  1241.                strerror (errno));
  1242.       (void) fflush (stderr);
  1243. #else
  1244.       extern int errno;
  1245.       extern char *sys_errlist[];
  1246.       extern int sys_nerr;
  1247.  
  1248.       char *ptr;
  1249.  
  1250.       ptr = ((errno >= 0 && errno < sys_nerr)
  1251.          ? sys_errlist[errno] : "Unknown error code");
  1252.       bad_input++;
  1253.       fprintf (stderr, "%s: can't read %s: %s\n", myname, name, ptr);
  1254. #endif
  1255.       return;
  1256.     }
  1257.     }
  1258.   
  1259.   while (read_pattern_space ())
  1260.     {
  1261.       execute_program (the_program);
  1262.       if (!no_default_output)
  1263.     ck_fwrite (line.text, 1, line.length, stdout);
  1264.       if (append.length)
  1265.     {
  1266.       ck_fwrite (append.text, 1, append.length, stdout);
  1267.       append.length = 0;
  1268.     }
  1269.       if (quit_cmd)
  1270.     break;
  1271.     }
  1272.   ck_fclose (input_file);
  1273. }
  1274.  
  1275. static char *
  1276. eol_pos (str, len)
  1277.      char *str;
  1278.      int len;
  1279. {
  1280.   while (len--)
  1281.     if (*str++ == '\n')
  1282.       return --str;
  1283.   return --str;
  1284. }
  1285.  
  1286. static void
  1287. chr_copy (dest, src, len)
  1288.      char *dest;
  1289.      char *src;
  1290.      int len;
  1291. {
  1292.   while (len--)
  1293.     *dest++ = *src++;
  1294. }
  1295.  
  1296. /* Execute the program 'vec' on the current input line. */
  1297. static struct re_registers regs =
  1298. {0, 0, 0};
  1299.  
  1300. void
  1301. execute_program (vec)
  1302.      struct vector *vec;
  1303. {
  1304.   struct sed_cmd *cur_cmd;
  1305.   int n;
  1306.   int addr_matched;
  1307.   static int end_cycle;
  1308.  
  1309.   int start;
  1310.   int remain;
  1311.   int offset;
  1312.  
  1313.   static struct line tmp;
  1314.   struct line t;
  1315.   char *rep, *rep_end, *rep_next, *rep_cur;
  1316.  
  1317.   int count;
  1318.   struct vector *restart_vec = vec;
  1319.  
  1320. restart:
  1321.   vec = restart_vec;
  1322.   count = 0;
  1323.  
  1324.   end_cycle = 0;
  1325.  
  1326.   for (cur_cmd = vec->v, n = vec->v_length; n; cur_cmd++, n--)
  1327.     {
  1328.     exe_loop:
  1329. #ifdef MPW
  1330.       SpinCursor (1);
  1331. #endif
  1332.       addr_matched = 0;
  1333.       if (cur_cmd->aflags & A1_MATCHED_BIT)
  1334.     {
  1335.       addr_matched = 1;
  1336.       if (match_address (&(cur_cmd->a2)))
  1337.         cur_cmd->aflags &= ~A1_MATCHED_BIT;
  1338.     }
  1339.       else if (match_address (&(cur_cmd->a1)))
  1340.     {
  1341.       addr_matched = 1;
  1342.       if (cur_cmd->a2.addr_type != addr_is_null)
  1343.         if (   (cur_cmd->a2.addr_type == addr_is_regex)
  1344.         || !match_address (&(cur_cmd->a2)))
  1345.           cur_cmd->aflags |= A1_MATCHED_BIT;
  1346.  
  1347.     }
  1348.       if (cur_cmd->aflags & ADDR_BANG_BIT)
  1349.     addr_matched = !addr_matched;
  1350.       if (!addr_matched)
  1351.     continue;
  1352.       switch (cur_cmd->cmd)
  1353.     {
  1354.     case '{':        /* Execute sub-program */
  1355.       if (cur_cmd->x.sub->v_length)
  1356.         {
  1357.           vec = cur_cmd->x.sub;
  1358.           cur_cmd = vec->v;
  1359.           n = vec->v_length;
  1360.           goto exe_loop;
  1361.         }
  1362.       break;
  1363.  
  1364.     case '}':
  1365.       cur_cmd = vec->return_v->v + vec->return_i;
  1366.       n = vec->return_v->v_length - vec->return_i;
  1367.       vec = vec->return_v;
  1368.       break;
  1369.  
  1370.     case ':':        /* Executing labels is easy. */
  1371.       break;
  1372.  
  1373.     case '=':
  1374.       printf ("%d\n", input_line_number);
  1375.       break;
  1376.  
  1377.     case 'a':
  1378.       while (append.alloc - append.length < cur_cmd->x.cmd_txt.text_len)
  1379.         {
  1380.           append.alloc *= 2;
  1381.           append.text = ck_realloc (append.text, append.alloc);
  1382.         }
  1383.       bcopy (cur_cmd->x.cmd_txt.text,
  1384.          append.text + append.length, cur_cmd->x.cmd_txt.text_len);
  1385.       append.length += cur_cmd->x.cmd_txt.text_len;
  1386.       break;
  1387.  
  1388.     case 'b':
  1389.       if (!cur_cmd->x.jump)
  1390.         end_cycle++;
  1391.       else
  1392.         {
  1393.           struct sed_label *j = cur_cmd->x.jump;
  1394.  
  1395.           n = j->v->v_length - j->v_index;
  1396.           cur_cmd = j->v->v + j->v_index;
  1397.           goto exe_loop;
  1398.         }
  1399.       break;
  1400.  
  1401.     case 'c':
  1402.       line.length = 0;
  1403.       if (!((cur_cmd->aflags & A1_MATCHED_BIT)))
  1404.         ck_fwrite (cur_cmd->x.cmd_txt.text,
  1405.                1, cur_cmd->x.cmd_txt.text_len, stdout);
  1406.       end_cycle++;
  1407.       break;
  1408.  
  1409.     case 'd':
  1410.       line.length = 0;
  1411.       end_cycle++;
  1412.       break;
  1413.  
  1414.     case 'D':
  1415.       {
  1416.         char *tmp;
  1417.         int newlength;
  1418.  
  1419.         tmp = eol_pos (line.text, line.length);
  1420.         newlength = line.length - (tmp - line.text) - 1;
  1421.         if (newlength)
  1422.           {
  1423.         chr_copy (line.text, tmp + 1, newlength);
  1424.         line.length = newlength;
  1425.         goto restart;
  1426.           }
  1427.         line.length = 0;
  1428.         end_cycle++;
  1429.       }
  1430.       break;
  1431.  
  1432.     case 'g':
  1433.       line_copy (&hold, &line);
  1434.       break;
  1435.  
  1436.     case 'G':
  1437.       line_append (&hold, &line);
  1438.       break;
  1439.  
  1440.     case 'h':
  1441.       line_copy (&line, &hold);
  1442.       break;
  1443.  
  1444.     case 'H':
  1445.       line_append (&line, &hold);
  1446.       break;
  1447.  
  1448.     case 'i':
  1449.       ck_fwrite (cur_cmd->x.cmd_txt.text, 1,
  1450.              cur_cmd->x.cmd_txt.text_len, stdout);
  1451.       break;
  1452.  
  1453.     case 'l':
  1454.       {
  1455.         char *tmp;
  1456.         int n;
  1457.         int width = 0;
  1458.  
  1459.         n = line.length;
  1460.         tmp = line.text;
  1461.         while (n--)
  1462.           {
  1463.         /* Skip the trailing newline, if there is one */
  1464.         if (!n && (*tmp == '\n'))
  1465.           break;
  1466.         if (width > 77)
  1467.           {
  1468.             width = 0;
  1469.             putchar ('\n');
  1470.           }
  1471.         if (*tmp == '\\')
  1472.           {
  1473.             printf ("\\\\");
  1474.             width += 2;
  1475.           }
  1476.         else if (isprint (*tmp))
  1477.           {
  1478.             putchar (*tmp);
  1479.             width++;
  1480.           }
  1481.         else
  1482.           switch (*tmp)
  1483.             {
  1484. #if 0
  1485.               /* Should print \00 instead of \0 because (a) POSIX */
  1486.               /* requires it, and (b) this way \01 is unambiguous.  */
  1487.             case '\0':
  1488.               printf ("\\0");
  1489.               width += 2;
  1490.               break;
  1491. #endif
  1492.             case 007:
  1493.               printf ("\\a");
  1494.               width += 2;
  1495.               break;
  1496.             case '\b':
  1497.               printf ("\\b");
  1498.               width += 2;
  1499.               break;
  1500.             case '\f':
  1501.               printf ("\\f");
  1502.               width += 2;
  1503.               break;
  1504.             case '\n':
  1505.               printf ("\\n");
  1506.               width += 2;
  1507.               break;
  1508.             case '\r':
  1509.               printf ("\\r");
  1510.               width += 2;
  1511.               break;
  1512.             case '\t':
  1513.               printf ("\\t");
  1514.               width += 2;
  1515.               break;
  1516.             case '\v':
  1517.               printf ("\\v");
  1518.               width += 2;
  1519.               break;
  1520.             default:
  1521.               printf ("\\%02x", (*tmp) & 0xFF);
  1522.               width += 2;
  1523.               break;
  1524.             }
  1525.         tmp++;
  1526.           }
  1527.         putchar ('\n');
  1528.       }
  1529.       break;
  1530.  
  1531.     case 'n':
  1532.       if (feof (input_file))
  1533.         goto quit;
  1534.       if (!no_default_output)
  1535.         ck_fwrite (line.text, 1, line.length, stdout);
  1536.       read_pattern_space ();
  1537.       break;
  1538.  
  1539.     case 'N':
  1540.       if (feof (input_file))
  1541.         {
  1542.           line.length = 0;
  1543.           goto quit;
  1544.         }
  1545.       append_pattern_space ();
  1546.       break;
  1547.  
  1548.     case 'p':
  1549.       ck_fwrite (line.text, 1, line.length, stdout);
  1550.       break;
  1551.  
  1552.     case 'P':
  1553.       {
  1554.         char *tmp;
  1555.  
  1556.         tmp = eol_pos (line.text, line.length);
  1557.         ck_fwrite (line.text, 1,
  1558.                tmp ? tmp - line.text + 1
  1559.                : line.length, stdout);
  1560.       }
  1561.       break;
  1562.  
  1563.     case 'q':
  1564.     quit:
  1565.       quit_cmd++;
  1566.       end_cycle++;
  1567.       break;
  1568.  
  1569.     case 'r':
  1570.       {
  1571.         int n = 0;
  1572.  
  1573.         if (cur_cmd->x.io_file)
  1574.           {
  1575.         rewind (cur_cmd->x.io_file);
  1576.         do
  1577.           {
  1578.             append.length += n;
  1579.             if (append.length == append.alloc)
  1580.               {
  1581.             append.alloc *= 2;
  1582.             append.text = ck_realloc (append.text, append.alloc);
  1583.               }
  1584.             n = fread (append.text + append.length, sizeof (char),
  1585.                    append.alloc - append.length,
  1586.                    cur_cmd->x.io_file);
  1587.           }
  1588.         while (n > 0);
  1589.         if (ferror (cur_cmd->x.io_file))
  1590.           panic ("Read error on input file to 'r' command");
  1591.           }
  1592.       }
  1593.       break;
  1594.  
  1595.     case 's':
  1596.       {
  1597.         int trail_nl_p = line.text [line.length - 1] == '\n';
  1598.         if (!tmp.alloc)
  1599.           {
  1600.         tmp.alloc = 50;
  1601.         tmp.text = ck_malloc (50);
  1602.           }
  1603.         count = 0;
  1604.         start = 0;
  1605.         remain = line.length - trail_nl_p;
  1606.         tmp.length = 0;
  1607.         rep = cur_cmd->x.cmd_regex.replacement;
  1608.         rep_end = rep + cur_cmd->x.cmd_regex.replace_length;
  1609.         
  1610.         while ((offset = re_search (cur_cmd->x.cmd_regex.regx,
  1611.                     line.text,
  1612.                     line.length - trail_nl_p,
  1613.                     start,
  1614.                     remain,
  1615.                     ®s)) >= 0)
  1616.           {
  1617.         count++;
  1618.         if (offset - start)
  1619.           str_append (&tmp, line.text + start, offset - start);
  1620.         
  1621.         if (cur_cmd->x.cmd_regex.flags & S_NUM_BIT)
  1622.           {
  1623.             if (count != cur_cmd->x.cmd_regex.numb)
  1624.               {
  1625.             int matched = regs.end[0] - regs.start[0];
  1626.             if (!matched) matched = 1;
  1627.             str_append (&tmp, line.text + regs.start[0], matched);
  1628.             start = (offset == regs.end[0]
  1629.                  ? offset + 1 : regs.end[0]);
  1630.             remain = (line.length - trail_nl_p) - start;
  1631.             continue;
  1632.               }
  1633.           }
  1634.         
  1635.         for (rep_next = rep_cur = rep; rep_next < rep_end; rep_next++)
  1636.           {
  1637.             if (*rep_next == '&')
  1638.               {
  1639.             if (rep_next - rep_cur)
  1640.               str_append (&tmp, rep_cur, rep_next - rep_cur);
  1641.             str_append (&tmp, line.text + regs.start[0], regs.end[0] - regs.start[0]);
  1642.             rep_cur = rep_next + 1;
  1643.               }
  1644.             else if (*rep_next == '\\')
  1645.               {
  1646.             if (rep_next - rep_cur)
  1647.               str_append (&tmp, rep_cur, rep_next - rep_cur);
  1648.             rep_next++;
  1649.             if (rep_next != rep_end)
  1650.               {
  1651.                 int n;
  1652.                 
  1653.                 if (*rep_next >= '0' && *rep_next <= '9')
  1654.                   {
  1655.                 n = *rep_next - '0';
  1656.                 str_append (&tmp, line.text + regs.start[n], regs.end[n] - regs.start[n]);
  1657.                   }
  1658.                 else
  1659.                   str_append (&tmp, rep_next, 1);
  1660.               }
  1661.             rep_cur = rep_next + 1;
  1662.               }
  1663.           }
  1664.         if (rep_next - rep_cur)
  1665.           str_append (&tmp, rep_cur, rep_next - rep_cur);
  1666.         if (offset == regs.end[0])
  1667.           {
  1668.             str_append (&tmp, line.text + offset, 1);
  1669.             ++regs.end[0];
  1670.           }
  1671.         start = regs.end[0];
  1672.         
  1673.         remain = (line.length - trail_nl_p) - start;
  1674.         if (remain < 0)
  1675.           break;
  1676.         if (!(cur_cmd->x.cmd_regex.flags & S_GLOBAL_BIT))
  1677.           break;
  1678.           }
  1679.         if (!count)
  1680.           break;
  1681.         replaced = 1;
  1682.         str_append (&tmp, line.text + start, remain + trail_nl_p);
  1683.         t.text = line.text;
  1684.         t.length = line.length;
  1685.         t.alloc = line.alloc;
  1686.         line.text = tmp.text;
  1687.         line.length = tmp.length;
  1688.         line.alloc = tmp.alloc;
  1689.         tmp.text = t.text;
  1690.         tmp.length = t.length;
  1691.         tmp.alloc = t.alloc;
  1692.         if ((cur_cmd->x.cmd_regex.flags & S_WRITE_BIT)
  1693.         && cur_cmd->x.cmd_regex.wio_file)
  1694.           ck_fwrite (line.text, 1, line.length,
  1695.              cur_cmd->x.cmd_regex.wio_file);
  1696.         if (cur_cmd->x.cmd_regex.flags & S_PRINT_BIT)
  1697.           ck_fwrite (line.text, 1, line.length, stdout);
  1698.         break;
  1699.       }
  1700.         
  1701.     case 't':
  1702.       if (replaced)
  1703.         {
  1704.           replaced = 0;
  1705.           if (!cur_cmd->x.jump)
  1706.         end_cycle++;
  1707.           else
  1708.         {
  1709.           struct sed_label *j = cur_cmd->x.jump;
  1710.  
  1711.           n = j->v->v_length - j->v_index;
  1712.           cur_cmd = j->v->v + j->v_index;
  1713.           goto exe_loop;
  1714.         }
  1715.         }
  1716.       break;
  1717.  
  1718.     case 'w':
  1719.       if (cur_cmd->x.io_file)
  1720.         ck_fwrite (line.text, 1, line.length, cur_cmd->x.io_file);
  1721.       break;
  1722.  
  1723.     case 'x':
  1724.       {
  1725.         struct line tmp;
  1726.  
  1727.         tmp = line;
  1728.         line = hold;
  1729.         hold = tmp;
  1730.       }
  1731.       break;
  1732.  
  1733.     case 'y':
  1734.       {
  1735.         unsigned char *p, *e;
  1736.  
  1737.         for (p = (unsigned char *) (line.text), e = p + line.length;
  1738.          p < e;
  1739.          p++)
  1740.           *p = cur_cmd->x.translate[*p];
  1741.       }
  1742.       break;
  1743.  
  1744.     default:
  1745.       panic ("INTERNAL ERROR: Bad cmd %c", cur_cmd->cmd);
  1746.     }
  1747.       if (end_cycle)
  1748.     break;
  1749.     }
  1750. }
  1751.  
  1752.  
  1753. /* Return non-zero if the current line matches the address
  1754.    pointed to by 'addr'. */
  1755. int
  1756. match_address (addr)
  1757.      struct addr *addr;
  1758. {
  1759.   switch (addr->addr_type)
  1760.     {
  1761.     case addr_is_null:
  1762.       return 1;
  1763.     case addr_is_num:
  1764.       return (input_line_number == addr->addr_number);
  1765.  
  1766.     case addr_is_regex:
  1767.       {
  1768.     int trail_nl_p = line.text [line.length - 1] == '\n';
  1769.     return (re_search (addr->addr_regex,
  1770.                line.text,
  1771.                line.length - trail_nl_p,
  1772.                0,
  1773.                line.length - trail_nl_p,
  1774.                (struct re_registers *) 0) >= 0) ? 1 : 0;
  1775.       }
  1776.     case addr_is_last:
  1777.       return (input_EOF) ? 1 : 0;
  1778.  
  1779.     default:
  1780.       panic ("INTERNAL ERROR: bad address type");
  1781.       break;
  1782.     }
  1783.   return -1;
  1784. }
  1785.  
  1786. /* Read in the next line of input, and store it in the
  1787.    pattern space.  Return non-zero if this is the last line of input */
  1788.  
  1789. int
  1790. read_pattern_space ()
  1791. {
  1792.   int n;
  1793.   char *p;
  1794.   int ch;
  1795.  
  1796.   p = line.text;
  1797.   n = line.alloc;
  1798.  
  1799.   if (feof (input_file))
  1800.     return 0;
  1801.   input_line_number++;
  1802.   replaced = 0;
  1803.   for (;;)
  1804.     {
  1805. #ifdef MPW
  1806.       SpinCursor (-1);
  1807. #endif
  1808.       if (n == 0)
  1809.     {
  1810.       line.text = ck_realloc (line.text, line.alloc * 2);
  1811.       p = line.text + line.alloc;
  1812.       n = line.alloc;
  1813.       line.alloc *= 2;
  1814.     }
  1815.       ch = getc (input_file);
  1816.       if (ch == EOF)
  1817.     {
  1818.       if (n == line.alloc)
  1819.         return 0;
  1820.       /* *p++ = '\n'; */
  1821.       /* --n; */
  1822.       line.length = line.alloc - n;
  1823.       if (last_input_file)
  1824.         input_EOF++;
  1825.       return 1;
  1826.     }
  1827.       *p++ = ch;
  1828.       --n;
  1829.       if (ch == '\n')
  1830.     {
  1831.       line.length = line.alloc - n;
  1832.       break;
  1833.     }
  1834.     }
  1835.   ch = getc (input_file);
  1836.   if (ch != EOF)
  1837.     ungetc (ch, input_file);
  1838.   else if (last_input_file)
  1839.     input_EOF++;
  1840.   return 1;
  1841. }
  1842.  
  1843. /* Inplement the 'N' command, which appends the next line of input to
  1844.    the pattern space. */
  1845. void
  1846. append_pattern_space ()
  1847. {
  1848.   char *p;
  1849.   int n;
  1850.   int ch;
  1851.  
  1852.   p = line.text + line.length;
  1853.   n = line.alloc - line.length;
  1854.  
  1855.   input_line_number++;
  1856.   replaced = 0;
  1857.   for (;;)
  1858.     {
  1859.       ch = getc (input_file);
  1860.       if (ch == EOF)
  1861.     {
  1862.       if (n == line.alloc)
  1863.         return;
  1864.       /* *p++ = '\n'; */
  1865.       /* --n; */
  1866.       line.length = line.alloc - n;
  1867.       if (last_input_file)
  1868.         input_EOF++;
  1869.       return;
  1870.     }
  1871.       if (n == 0)
  1872.     {
  1873.       line.text = ck_realloc (line.text, line.alloc * 2);
  1874.       p = line.text + line.alloc;
  1875.       n = line.alloc;
  1876.       line.alloc *= 2;
  1877.     }
  1878.       *p++ = ch;
  1879.       --n;
  1880.       if (ch == '\n')
  1881.     {
  1882.       line.length = line.alloc - n;
  1883.       break;
  1884.     }
  1885.     }
  1886.   ch = getc (input_file);
  1887.   if (ch != EOF)
  1888.     ungetc (ch, input_file);
  1889.   else if (last_input_file)
  1890.     input_EOF++;
  1891. }
  1892.  
  1893. /* Copy the contents of the line 'from' into the line 'to'.
  1894.    This destroys the old contents of 'to'.  It will still work
  1895.    if the line 'from' contains nulls. */
  1896. void
  1897. line_copy (from, to)
  1898.      struct line *from, *to;
  1899. {
  1900.   if (from->length > to->alloc)
  1901.     {
  1902.       to->alloc = from->length;
  1903.       to->text = ck_realloc (to->text, to->alloc);
  1904.     }
  1905.   bcopy (from->text, to->text, from->length);
  1906.   to->length = from->length;
  1907. }
  1908.  
  1909. /* Append the contents of the line 'from' to the line 'to'.
  1910.    This routine will work even if the line 'from' contains nulls */
  1911. void
  1912. line_append (from, to)
  1913.      struct line *from, *to;
  1914. {
  1915.   if (from->length > (to->alloc - to->length))
  1916.     {
  1917.       to->alloc += from->length;
  1918.       to->text = ck_realloc (to->text, to->alloc);
  1919.     }
  1920.   bcopy (from->text, to->text + to->length, from->length);
  1921.   to->length += from->length;
  1922. }
  1923.  
  1924. /* Append 'length' bytes from 'string' to the line 'to'
  1925.    This routine *will* append bytes with nulls in them, without
  1926.    failing. */
  1927. void
  1928. str_append (to, string, length)
  1929.      struct line *to;
  1930.      char *string;
  1931.      int length;
  1932. {
  1933.   if (length > to->alloc - to->length)
  1934.     {
  1935.       to->alloc += length;
  1936.       to->text = ck_realloc (to->text, to->alloc);
  1937.     }
  1938.   bcopy (string, to->text + to->length, length);
  1939.   to->length += length;
  1940. }
  1941.  
  1942. void
  1943. usage (status)
  1944.      int status;
  1945. {
  1946. #ifdef MPW
  1947.   fprintf (status ? stderr : stdout, "\
  1948. # Usage - %s [-nV] [--quiet] [--silent] [--version] [-e script]\n\
  1949. #         [-f script-file] [--expression=script] [--file=script-file] [file…]\n",
  1950. #else
  1951.   fprintf (status ? stderr : stdout, "\
  1952. Usage: %s [-nV] [--quiet] [--silent] [--version] [-e script]\n\
  1953.         [-f script-file] [--expression=script] [--file=script-file] [file...]\n",
  1954. #endif
  1955.        myname);
  1956.   exit (status);
  1957. }
  1958.